#include "syntaxtree.h"
#include "basicparser.h"
#include "parser.h"
#include "tokenizer.h"
#include "utills.h"
#include "errors.h"

namespace CCompiler
{

    SymType *SynAssignOp::getType(SymTable &tbl)
    {
        SymType *left = first_->getType(tbl);
        SymType *right = second_->getType(tbl);
        if(!first_->isLvalue())
            throw Errors::SyntaxError("left of " + binOp_ + " must be lvalue");
        if(binOp_ != "=")
        {
            BinOpOnTypes boot(AssignOp2BinOp[binOp_], BinOpTypePair(left->getSymbolType(), right->getSymbolType()));
            if(!AvailableBinOpsAndHandlers.count(boot))
                throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + left->getName() + ",right=" +
                right->getName() +')');
            right = AvailableBinOpsAndHandlers[boot](first_, second_, tbl);
        }
        BinOpOnTypes boot(binOp_, BinOpTypePair(left->getSymbolType(), right->getSymbolType()));
        if(!AvailableBinOpsAndHandlers.count(boot))
            throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + left->getName() + ",right=" +
            right->getName() +')');
        return AvailableBinOpsAndHandlers[boot](first_, second_, tbl);              
    }

    SymType *SynBinaryOp::getType(SymTable &tbl)
    {
        SymType *ft = first_->getType(tbl)->getType(), *st = second_->getType(tbl)->getType();
        BinOpOnTypes boot(binOp_, BinOpTypePair(ft->getSymbolType(),
            st->getSymbolType()));
        if(!AvailableBinOpsAndHandlers.count(boot))
            throw Errors::SyntaxError("'" + binOp_ + '\'' + " undefined(left=" + ft->getName() + ",right=" +
            st->getName() +')');
        return AvailableBinOpsAndHandlers[boot](first_, second_, tbl);
    }

    SymType *SynUnaryOp::getType(SymTable &tbl)
    { 
        UnaryOpOnType uoot(unOp_, first_->getType(tbl)->getSymbolType());
        if((unOp_ == "++" || unOp_ == "--" || unOp_ == "&") && !first_->isLvalue())
            throw Errors::SyntaxError("Operand of '" + unOp_ + "' must be lvalue");
        if(!AvailableUnaryOpsAndHandlers.count(uoot))
            throw Errors::SyntaxError("'" + unOp_ + '\'' + " undefined(operand=" + first_->getType(tbl)->getName() + ")");
        return AvailableUnaryOpsAndHandlers[uoot](first_, tbl);
    }


    void SynBinaryOp::print(std::ostream &stream, int ident)
    {
        second_->print(stream, ident + 1);
        makeIDent(stream, ident);
        stream << binOp_ << '\n';
        first_->print(stream, ident + 1);
    }

    void SynUnaryOp::print(std::ostream &stream, int ident)
    {
        if(postfix_)
            first_->print(stream, ident + 1);
        makeIDent(stream, ident);
        stream << unOp_<< '\n';
        if(!postfix_)
            first_->print(stream, ident + 1);
    }

    void SynSizeof::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << "sizeof" << '\n';
        type_->print(stream, ident+1);
        stream << '\n';
    }

    void SynFunctionCall::print( std::ostream &stream, int ident /*= 0*/ )
    {
        for(ArgList::reverse_iterator it = arguements_.rbegin(); it != arguements_.rend(); ++it)
            (*it)->print(stream, ident + 1);
        makeIDent(stream, ident);
        stream << "()" << '\n';
        caller_->print(stream, ident + 1);
    }

    void SynVal::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << identifier_ << '\n';
    }
    void SynInt::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << value_ << '\n';
    }
    void SynChar::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << value_ << '\n';
    }
    void SynFloat::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << value_ << '\n';
    }
    void SynString::print(std::ostream &stream, int ident)
    {
        makeIDent(stream, ident);
        stream << value_ << '\n';
    }
}
